; ==================================================================
; The Mike Operating System bootloader
; Copyright (C) 2006 - 2012 MikeOS Developers -- see doc/LICENSE.TXT
; POS32 OS BootLoader (C) 2013 Alldesplay Corporation
; Based on a free boot loader by E Dehling. It scans the FAT12
; floppy for NTLOAD.BIN (the MultiBootLoader), loads it and executes it.
; This must grow no larger than 512 bytes (one sector), with the final
; two bytes being the boot signature (AA55h). Note that in FAT12,
; a cluster is the same as a sector: 512 bytes.
; ==================================================================


        BITS 16

        jmp short bootloader_start        ; Jump past disk description section
        nop                                ; Pad out before disk description


; ------------------------------------------------------------------
; Disk description table, to make it a valid floppy
; Note: some of these values are hard-coded in the source!
; Values are those used by IBM for 1.44 MB, 3.5" diskette

OEMLabel                db "MIKEBOOT"        ; Disk label
BytesPerSector                dw 512                ; Bytes per sector
SectorsPerCluster        db 1                ; Sectors per cluster
ReservedForBoot                dw 1                ; Reserved sectors for boot record
NumberOfFats                db 2                ; Number of copies of the FAT
RootDirEntries                dw 224                ; Number of entries in root dir
                                        ; (224 * 32 = 7168 = 14 sectors to read)
LogicalSectors                dw 2880                ; Number of logical sectors
MediumByte                db 0F0h                ; Medium descriptor byte
SectorsPerFat                dw 9                ; Sectors per FAT
SectorsPerTrack                dw 18                ; Sectors per track (36/cylinder)
Sides                        dw 2                ; Number of sides/heads
HiddenSectors                dd 0                ; Number of hidden sectors
LargeSectors                dd 0                ; Number of LBA sectors
DriveNo                        dw 0                ; Drive No: 0
Signature                db 41                ; Drive signature: 41 for floppy
VolumeID                dd 00000000h        ; Volume ID: any number
VolumeLabel                db "MIKEOS     "; Volume Label: any 11 chars
FileSystem                db "FAT12   "        ; File system type: don't change!


; ------------------------------------------------------------------
; Main bootloader code

bootloader_start:
        mov ax, 07C0h                        ; Set up 4K of stack space above buffer
        add ax, 544                        ; 8k buffer = 512 paragraphs + 32 paragraphs (loader)
        cli                                ; Disable interrupts while changing stack
        mov ss, ax
        mov sp, 4096
        sti                                ; Restore interrupts

        mov ax, 07C0h                        ; Set data segment to where we're loaded
        mov ds, ax

        ; NOTE: A few early BIOSes are reported to improperly set DL

        cmp dl, 0
        je no_change
        mov [bootdev], dl                ; Save boot device number
        mov ah, 8                        ; Get drive parameters
        int 13h
        jc fatal_disk_error
        and cx, 3Fh                        ; Maximum sector number
        mov [SectorsPerTrack], cx        ; Sector numbers start at 1
        movzx dx, dh                        ; Maximum head number
        add dx, 1                        ; Head numbers start at 0 - add 1 for total
        mov [Sides], dx

no_change:
        mov eax, 0                        ; Needed for some older BIOSes


; First, we need to load the root directory from the disk. Technical details:
; Start of root = ReservedForBoot + NumberOfFats * SectorsPerFat = logical 19
; Number of root = RootDirEntries * 32 bytes/entry / 512 bytes/sector = 14
; Start of user data = (start of root) + (number of root) = logical 33

floppy_ok:                                ; Ready to read first block of data
        mov ax, 19                        ; Root dir starts at logical sector 19
        call l2hts

        mov si, buffer                        ; Set ES:BX to point to our buffer (see end of code)
        mov bx, ds
        mov es, bx
        mov bx, si

        mov ah, 2                        ; Params for int 13h: read floppy sectors
        mov al, 14                        ; And read 14 of them

        pusha                                ; Prepare to enter loop


read_root_dir:
        popa                                ; In case registers are altered by int 13h
        pusha

        stc                                ; A few BIOSes do not set properly on error
        int 13h                                ; Read sectors using BIOS

        jnc search_dir                        ; If read went OK, skip ahead
        call reset_floppy                ; Otherwise, reset floppy controller and try again
        jnc read_root_dir                ; Floppy reset OK?

        jmp reboot                        ; If not, fatal double error


search_dir:
        popa

        mov ax, ds                        ; Root dir is now in [buffer]
        mov es, ax                        ; Set DI to this info
        mov di, buffer

        mov cx, word [RootDirEntries]        ; Search all (224) entries
        mov ax, 0                        ; Searching at offset 0


next_root_entry:
        xchg cx, dx                        ; We use CX in the inner loop...

        mov si, kern_filename                ; Start searching for kernel filename
        mov cx, 11
        rep cmpsb
        je found_file_to_load                ; Pointer DI will be at offset 11

        add ax, 32                        ; Bump searched entries by 1 (32 bytes per entry)

        mov di, buffer                        ; Point to next entry
        add di, ax

        xchg dx, cx                        ; Get the original CX back
        loop next_root_entry

        mov si, file_not_found                ; If kernel is not found, bail out
        call print_string
        jmp reboot


found_file_to_load:                        ; Fetch cluster and load FAT into RAM
        mov ax, word [es:di+0Fh]        ; Offset 11 + 15 = 26, contains 1st cluster
        mov word [cluster], ax

        mov ax, 1                        ; Sector 1 = first sector of first FAT
        call l2hts

        mov di, buffer                        ; ES:BX points to our buffer
        mov bx, di

        mov ah, 2                        ; int 13h params: read (FAT) sectors
        mov al, 9                        ; All 9 sectors of 1st FAT

        pusha                                ; Prepare to enter loop


read_fat:
        popa                                ; In case registers are altered by int 13h
        pusha

        stc
        int 13h                                ; Read sectors using the BIOS

        jnc read_fat_ok                        ; If read went OK, skip ahead
        call reset_floppy                ; Otherwise, reset floppy controller and try again
        jnc read_fat                        ; Floppy reset OK?

; ******************************************************************
fatal_disk_error:
; ******************************************************************
        mov si, disk_error                ; If not, print error message and reboot
        call print_string
        jmp reboot                        ; Fatal double error


read_fat_ok:
        popa

        mov ax, 1000h                        ; Segment where we'll load the kernel
        mov es, ax
        mov bx, 0

        mov ah, 2                        ; int 13h floppy read params
        mov al, 1

        push ax                                ; Save in case we (or int calls) lose it


; Now we must load the FAT from the disk. Here's how we find out where it starts:
; FAT cluster 0 = media descriptor = 0F0h
; FAT cluster 1 = filler cluster = 0FFh
; Cluster start = ((cluster number) - 2) * SectorsPerCluster + (start of user)
;               = (cluster number) + 31

load_file_sector:
        mov ax, word [cluster]                ; Convert sector to logical
        add ax, 31

        call l2hts                        ; Make appropriate params for int 13h

        mov ax, 1000h                        ; Set buffer past what we've already read
        mov es, ax
        mov bx, word [pointer]

        pop ax                                ; Save in case we (or int calls) lose it
        push ax

        stc
        int 13h

        jnc calculate_next_cluster        ; If there's no error...

        call reset_floppy                ; Otherwise, reset floppy and retry
        jmp load_file_sector


        ; In the FAT, cluster values are stored in 12 bits, so we have to
        ; do a bit of maths to work out whether we're dealing with a byte
        ; and 4 bits of the next byte -- or the last 4 bits of one byte
        ; and then the subsequent byte!

calculate_next_cluster:
        mov ax, [cluster]
        mov dx, 0
        mov bx, 3
        mul bx
        mov bx, 2
        div bx                                ; DX = [cluster] mod 2
        mov si, buffer
        add si, ax                        ; AX = word in FAT for the 12 bit entry
        mov ax, word [ds:si]

        or dx, dx                        ; If DX = 0 [cluster] is even; if DX = 1 then it's odd

        jz even                                ; If [cluster] is even, drop last 4 bits of word
                                        ; with next cluster; if odd, drop first 4 bits

odd:
        shr ax, 4                        ; Shift out first 4 bits (they belong to another entry)
        jmp short next_cluster_cont


even:
        and ax, 0FFFh                        ; Mask out final 4 bits


next_cluster_cont:
        mov word [cluster], ax                ; Store cluster

        cmp ax, 0FF8h                        ; FF8h = end of file marker in FAT12
        jae end

        add word [pointer], 512                ; Increase buffer pointer 1 sector length
        jmp load_file_sector


end:                                        ; We've got the file to load!
        pop ax                                ; Clean up the stack (AX was pushed earlier)
        mov dl, byte [bootdev]                ; Provide kernel with boot device info

        jmp 1000h:0000h                        ; Jump to entry point of loaded kernel!


; ------------------------------------------------------------------
; BOOTLOADER SUBROUTINES

reboot:
        mov ax, 0
        int 16h                                ; Wait for keystroke
        mov ax, 0
        int 19h                                ; Reboot the system


print_string:                                ; Output string in SI to screen
        pusha

        mov ah, 0Eh                        ; int 10h teletype function

.repeat:
        lodsb                                ; Get char from string
        cmp al, 0
        je .done                        ; If char is zero, end of string
        int 10h                                ; Otherwise, print it
        jmp short .repeat

.done:
        popa
        ret


reset_floppy:                ; IN: [bootdev] = boot device; OUT: carry set on error
        push ax
        push dx
        mov ax, 0
        mov dl, byte [bootdev]
        stc
        int 13h
        pop dx
        pop ax
        ret


l2hts:                        ; Calculate head, track and sector settings for int 13h
                        ; IN: logical sector in AX, OUT: correct registers for int 13h
        push bx
        push ax

        mov bx, ax                        ; Save logical sector

        mov dx, 0                        ; First the sector
        div word [SectorsPerTrack]
        add dl, 01h                        ; Physical sectors start at 1
        mov cl, dl                        ; Sectors belong in CL for int 13h
        mov ax, bx

        mov dx, 0                        ; Now calculate the head
        div word [SectorsPerTrack]
        mov dx, 0
        div word [Sides]
        mov dh, dl                        ; Head/side
        mov ch, al                        ; Track

        pop ax
        pop bx

        mov dl, byte [bootdev]                ; Set correct device

        ret


; ------------------------------------------------------------------
; STRINGS AND VARIABLES

        kern_filename        db "NTLOAD  SYS"        ; MikeOS kernel filename

        disk_error        db "Floppy error! Press any key...", 0
        file_not_found        db "KERNEL.BIN not found!", 0

        bootdev                db 0         ; Boot device number
        cluster                dw 0         ; Cluster of the file we want to load
        pointer                dw 0         ; Pointer into Buffer, for loading kernel


; ------------------------------------------------------------------
; END OF BOOT SECTOR AND BUFFER START

        times 510-($-$$) db 0        ; Pad remainder of boot sector with zeros
        dw 0AA55h                ; Boot signature (DO NOT CHANGE!)


buffer:                                ; Disk buffer begins (8k after this, stack starts)